﻿#include "common.h"

// 测试 AES 加密解密
// 加密保存私钥到文件，写入密文长度，便于解密时读取
#define AES_BLOCK_SIZE 16

// 函数名: pad_buffer
// 功能: 对输入的缓冲区进行PKCS#7填充，使其长度达到指定的填充长度。
// 参数:
//   input: 输入的缓冲区，待填充的数据。
//   input_len: 输入缓冲区的长度。
//   output: 输出缓冲区，填充后的数据。
//   padded_len: 填充后的缓冲区长度，必须是AES_BLOCK_SIZE的倍数。
static void pad_buffer(const uint8_t* input, size_t input_len, uint8_t* output, size_t padded_len) {
    // 计算需要填充的字节数
    size_t pad = padded_len - input_len;
    // 将输入缓冲区的内容复制到输出缓冲区
    memcpy(output, input, input_len);
    // 从input_len开始，向输出缓冲区填充相同的pad值，直到达到padded_len
    for (size_t i = input_len; i < padded_len; ++i) {
        output[i] = (uint8_t)pad;
    }
}

// 函数名: unpad_buffer
// 功能: 移除PKCS#7填充，恢复原始数据长度。
// 参数:
//       buffer: 待处理的缓冲区，包含填充后的数据。
//       len: 输入输出参数，指向缓冲区当前长度的指针，函数会更新其为原始数据长度。
static void unpad_buffer(uint8_t* buffer, size_t* len) {
    // 获取填充的字节数，即最后一个字节的值
    uint8_t pad = buffer[*len - 1];
    // 检查填充字节是否在有效范围内（大于0且小于等于AES_BLOCK_SIZE）
    if (pad > 0 && pad <= AES_BLOCK_SIZE) {
        // 减去填充长度以恢复原始数据长度
        *len -= pad;
        // 在原始数据末尾添加字符串结束符'\0'
        buffer[*len] = '\0';
    }
}


// 测试AES加密和解密过程。
void test_aes() {
    // 打印测试开始信息
    print_start_line("AES加密解密测试开始");
    // 定义原始字符串
    const char* plain_text = "这是一串测试加密解密的字符";
    // 计算原始字符串的长度
    size_t pt_len = strlen(plain_text);
    // 计算填充后的数据长度，确保是AES_BLOCK_SIZE（通常为16字节）的倍数
    size_t padded_len = ((pt_len + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
    // 定义一个用于存储原始数据和填充后数据的缓冲区
    uint8_t pt_buf[128] = { 0 };
    // 调用 pad_buffer 函数对原始数据进行填充
    pad_buffer((const uint8_t*)plain_text, pt_len, pt_buf, padded_len);

    // 初始化一个用于存储密钥的缓冲区，密钥长度为16字节
    uint8_t key[16] = { 0 };
    // 将示例密钥复制到密钥缓冲区（实际应用中应使用随机密钥）
    memcpy(key, "1234567890abcdef", 16);

    // 定义一个用于存储加密后密文的缓冲区
    uint8_t ct_buf[128] = { 0 };
    // 调用 aesEncrypt 函数对填充后的数据进行加密
    int enc_ret = aesEncrypt(key, 16, pt_buf, ct_buf, (uint32_t)padded_len);
    // 如果加密失败，打印错误信息并返回
    if (enc_ret != 0) {
        printf("加密失败\n");
        return;
    }
    // 打印加密后的密文（十六进制表示）
    printHex(ct_buf, (int)padded_len, "加密后密文(十六进制)");

    // 定义一个用于存储解密后数据的缓冲区
    uint8_t dec_buf[128] = { 0 };
    // 调用aesDecrypt函数对密文进行解密
    int dec_ret = aesDecrypt(key, 16, ct_buf, dec_buf, (uint32_t)padded_len);
    // 如果解密失败，打印错误信息并返回
    if (dec_ret != 0) {
        printf("解密失败\n");
        return;
    }
    // 调用 unpad_buffer 函数移除填充, 并恢复原始数据长度
    unpad_buffer(dec_buf, &padded_len);
    // 打印解密后的明文
    printf("解密后明文: %s\n", dec_buf);
    // 打印测试结束信息
    print_end_line("AES加密解密测试结束");
}



// 获取文件大小函数（字节数）
// 方法一：使用 fseek 和 ftell 函数手动移动文件指针
static size_t get_file_size(FILE* file) {
    // 将文件指针移动到文件末尾
    fseek(file, 0, SEEK_END);
    // 获取文件指针的位置，即文件大小
    size_t size = ftell(file);
    // 将文件指针重新移动到文件开头
    fseek(file, 0, SEEK_SET);
    return size;
}

// 方法二：使用 stat 适用于 Linux 和 macOS
static size_t get_file_size_stat(const char *filename) {
    struct stat st;
    if (stat(filename, &st) == 0) {
        return st.st_size;
    }
    else {
        perror("无法获取文件状态");
        return 0;
    }
}

// 方法三：使用 _filelength 函数（适用于Windows系统）
#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64)
static size_t get_file_size_windows(const char *filename) {
    int fd = _open(filename, _O_RDONLY);
    if (fd == -1) {
        perror("无法打开文件");
        return 0;
    }

    size_t size = _filelength(fd);
    _close(fd);
    return size;
}
#endif


// 测试文件大小获取函数
void test_file_size() {
    // 打印测试开始信息
    print_start_line("文件大小测试开始");
    // 定义文件名
    const char* filename = "main.c";
    // 打开文件
    const FILE* file = fopen(filename, "rb");
    if (file == NULL) {
        perror("无法打开文件");
        return;
    }
    // 获取文件大小
    size_t size = get_file_size(file);
    // 打印文件大小
    printf("fseek 和 ftell 函数测试文件大小: %zu\n", size);
    size = get_file_size_stat(filename);
    printf("stat 函数测试文件大小: %zu\n", size);
    // 关闭文件
    fclose(file);
#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(WIN64)
    size = get_file_size_windows(filename);
    printf("_filelength 函数测试文件大小: %zu\n", size);
#endif
    // 打印测试结束信息
    print_end_line("文件大小获取测试结束");
}

